Este informe detalla el estudio realizado sobre un dataset que recoge información relevante de las viviendas en la ciudad de Agnes, en Iowa (Estados Unidos). El estudio, intentará mediante un modelo de regresión lineal múltiple, predecir el valor de la vivienda en función de un gran número de variables (tanto continuas como categóricas)
Para el control de versiones utilizaremos github. El repositorio se encuentra en https://github.com/AnaFernandezCruz/MetodosAnalisisDatos .
Antes de empezar a realizar el estudio debemos unificar los datos. Inicialmente los disponemos en tres archivos csv, ‘train.csv’, ‘test.csv’ y ‘sample_submission.csv’. Una vez unificados los datos bajo un mismo dataset, separaremos los datos en dos. Uno para realizar el estudio que más adelante lo utilizaremos como train y test, este sera el 90% del dataset completo. Y el segundo para validación, lo utilizaremos para validar nuestro modelo y sera el 10% del dataset completo.
# Cargamos los tres archivos
train_kaggle_ok <- read_csv('./Dataset/train.csv')
test_kaggle <- read_csv('./Dataset/test.csv')
test_kaggle_pk <- read_csv('./Dataset/sample_submission.csv')
# Merge el dataset test y sample_submission, ahora tendremos un dataset completo
test_kaggle_ok <- merge(test_kaggle, test_kaggle_pk, by="Id")
# Unificamos los dos dataset train.csv y test.csv
full_dataset <- rbind(train_kaggle_ok, test_kaggle_ok)
#Dividimos el dataset 10% - 90%
list_split <- full_dataset %>% split_dataframe(.1)
validation <- list_split[[1]] # 10%
dataset <- list_split[[2]] # 90%
#Guardamos el dataset de validacion en un archivo csv
write.csv(validation, './validation.csv', row.names=F)
El dataset que utilizaremos para realizar el estudio contiene 2628 observaciones y está dividido en 81 variables distintas, en las que una de ellas es la clave principal (denominada Id) que no debermos tener en cuenta en el análisis de regresión. De estas variables, 43 son discretas y 38 son contínuas. Vemos a simple vista que hay una gran cantidad de “missings” (datos faltantes), tendremos que analizar estas variables para una posible imputación de datos.
Observando la documentación del dataset y los datos una vez cargados, nos damos cuenta que algunas variables categoricas tiene una categoria denominada ‘NA’, cuando cargamos los datos, el programa traduce estas categorias a datos faltantes y no es cierto.
Variables afectadas:
Alley: NA-> No alley access, BsmtQual: NA-> No Basement, BsmtCond: NA-> No Basement, BsmtExposure: NA-> No Basement, BsmtFinType1: NA-> No Basement, BsmtFinType2: NA-> No Basement, GarageType: NA-> No Garage, GarageFinish: NA-> No Garage, GarageQual: NA-> No Garage, GarageCond: NA-> No Garage, PoolQC: NA-> No Pool, Fence: NA-> No Fence, MiscFeature: NA-None
Una vez cargados los datos, imputaremos a esas columnas, en los datos faltantes, la categoria NA. Supondremos que para estas columnas no existe ningún valor genuinamente vacío.
cols_remove_nas <-c('Alley','BsmtQual','BsmtCond','BsmtExposure','BsmtFinType1','BsmtFinType2','GarageType','GarageFinish','GarageQual','GarageCond','PoolQC','Fence','MiscFeature','FireplaceQu')
dataset <- dataset %>% dplyr::mutate_at(cols_remove_nas,
list(~ifelse(is.na(.), 'NA',.)) )
Una vez que nos aseguramos la integridad de los datos en todo el conjunto del estudio, separamos estos datos en dos grupos: train (70%) y test (30%).
#Dividimos el dataset 70% - 30%
list_split <- dataset %>% split_dataframe(.7)
train <- list_split[[1]] # 70%
test <- list_split[[2]] # 30%
summary(dataset)
## Id MSSubClass MSZoning LotFrontage
## Min. : 1.0 Min. : 20.0 Length:2628 Min. : 21.00
## 1st Qu.: 754.8 1st Qu.: 20.0 Class :character 1st Qu.: 59.00
## Median :1475.5 Median : 50.0 Mode :character Median : 68.00
## Mean :1475.5 Mean : 56.8 Mean : 69.33
## 3rd Qu.:2206.2 3rd Qu.: 70.0 3rd Qu.: 80.00
## Max. :2919.0 Max. :190.0 Max. :313.00
## NA's :441
## LotArea Street Alley LotShape
## Min. : 1300 Length:2628 Length:2628 Length:2628
## 1st Qu.: 7445 Class :character Class :character Class :character
## Median : 9436 Mode :character Mode :character Mode :character
## Mean : 10156
## 3rd Qu.: 11558
## Max. :215245
##
## LandContour Utilities LotConfig LandSlope
## Length:2628 Length:2628 Length:2628 Length:2628
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## Neighborhood Condition1 Condition2 BldgType
## Length:2628 Length:2628 Length:2628 Length:2628
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## HouseStyle OverallQual OverallCond YearBuilt
## Length:2628 Min. : 1.000 Min. :1.00 Min. :1872
## Class :character 1st Qu.: 5.000 1st Qu.:5.00 1st Qu.:1954
## Mode :character Median : 6.000 Median :5.00 Median :1973
## Mean : 6.089 Mean :5.58 Mean :1971
## 3rd Qu.: 7.000 3rd Qu.:6.00 3rd Qu.:2001
## Max. :10.000 Max. :9.00 Max. :2010
##
## YearRemodAdd RoofStyle RoofMatl Exterior1st
## Min. :1950 Length:2628 Length:2628 Length:2628
## 1st Qu.:1965 Class :character Class :character Class :character
## Median :1993 Mode :character Mode :character Mode :character
## Mean :1984
## 3rd Qu.:2004
## Max. :2010
##
## Exterior2nd MasVnrType MasVnrArea ExterQual
## Length:2628 Length:2628 Min. : 0.0 Length:2628
## Class :character Class :character 1st Qu.: 0.0 Class :character
## Mode :character Mode :character Median : 0.0 Mode :character
## Mean : 102.7
## 3rd Qu.: 164.0
## Max. :1600.0
## NA's :20
## ExterCond Foundation BsmtQual BsmtCond
## Length:2628 Length:2628 Length:2628 Length:2628
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## BsmtExposure BsmtFinType1 BsmtFinSF1 BsmtFinType2
## Length:2628 Length:2628 Min. : 0.0 Length:2628
## Class :character Class :character 1st Qu.: 0.0 Class :character
## Mode :character Mode :character Median : 371.0 Mode :character
## Mean : 445.4
## 3rd Qu.: 736.5
## Max. :5644.0
## NA's :1
## BsmtFinSF2 BsmtUnfSF TotalBsmtSF Heating
## Min. : 0.00 Min. : 0.0 Min. : 0 Length:2628
## 1st Qu.: 0.00 1st Qu.: 222.0 1st Qu.: 796 Class :character
## Median : 0.00 Median : 475.0 Median : 990 Mode :character
## Mean : 47.29 Mean : 562.9 Mean :1056
## 3rd Qu.: 0.00 3rd Qu.: 808.0 3rd Qu.:1300
## Max. :1474.00 Max. :2336.0 Max. :6110
## NA's :1 NA's :1 NA's :1
## HeatingQC CentralAir Electrical 1stFlrSF
## Length:2628 Length:2628 Length:2628 Min. : 334
## Class :character Class :character Class :character 1st Qu.: 880
## Mode :character Mode :character Mode :character Median :1082
## Mean :1163
## 3rd Qu.:1389
## Max. :5095
##
## 2ndFlrSF LowQualFinSF GrLivArea BsmtFullBath
## Min. : 0.0 Min. : 0.000 Min. : 334 Min. :0.0000
## 1st Qu.: 0.0 1st Qu.: 0.000 1st Qu.:1125 1st Qu.:0.0000
## Median : 0.0 Median : 0.000 Median :1441 Median :0.0000
## Mean : 332.9 Mean : 4.136 Mean :1500 Mean :0.4311
## 3rd Qu.: 702.2 3rd Qu.: 0.000 3rd Qu.:1740 3rd Qu.:1.0000
## Max. :2065.0 Max. :697.000 Max. :5642 Max. :3.0000
## NA's :2
## BsmtHalfBath FullBath HalfBath BedroomAbvGr
## Min. :0.00000 Min. :0.000 Min. :0.000 Min. :0.000
## 1st Qu.:0.00000 1st Qu.:1.000 1st Qu.:0.000 1st Qu.:2.000
## Median :0.00000 Median :2.000 Median :0.000 Median :3.000
## Mean :0.06055 Mean :1.567 Mean :0.379 Mean :2.857
## 3rd Qu.:0.00000 3rd Qu.:2.000 3rd Qu.:1.000 3rd Qu.:3.000
## Max. :2.00000 Max. :4.000 Max. :2.000 Max. :8.000
## NA's :2
## KitchenAbvGr KitchenQual TotRmsAbvGrd Functional
## Min. :0.000 Length:2628 Min. : 2.00 Length:2628
## 1st Qu.:1.000 Class :character 1st Qu.: 5.00 Class :character
## Median :1.000 Mode :character Median : 6.00 Mode :character
## Mean :1.045 Mean : 6.44
## 3rd Qu.:1.000 3rd Qu.: 7.00
## Max. :3.000 Max. :15.00
##
## Fireplaces FireplaceQu GarageType GarageYrBlt
## Min. :0.0000 Length:2628 Length:2628 Min. :1895
## 1st Qu.:0.0000 Class :character Class :character 1st Qu.:1960
## Median :1.0000 Mode :character Mode :character Median :1979
## Mean :0.5978 Mean :1978
## 3rd Qu.:1.0000 3rd Qu.:2002
## Max. :4.0000 Max. :2207
## NA's :138
## GarageFinish GarageCars GarageArea GarageQual
## Length:2628 Min. :0.000 Min. : 0.0 Length:2628
## Class :character 1st Qu.:1.000 1st Qu.: 322.0 Class :character
## Mode :character Median :2.000 Median : 480.0 Mode :character
## Mean :1.769 Mean : 473.4
## 3rd Qu.:2.000 3rd Qu.: 576.0
## Max. :5.000 Max. :1488.0
## NA's :1 NA's :1
## GarageCond PavedDrive WoodDeckSF OpenPorchSF
## Length:2628 Length:2628 Min. : 0.0 Min. : 0.00
## Class :character Class :character 1st Qu.: 0.0 1st Qu.: 0.00
## Mode :character Mode :character Median : 0.0 Median : 26.00
## Mean : 93.1 Mean : 47.43
## 3rd Qu.:168.0 3rd Qu.: 70.00
## Max. :870.0 Max. :742.00
##
## EnclosedPorch 3SsnPorch ScreenPorch PoolArea
## Min. : 0.00 Min. : 0.000 Min. : 0.00 Min. : 0.000
## 1st Qu.: 0.00 1st Qu.: 0.000 1st Qu.: 0.00 1st Qu.: 0.000
## Median : 0.00 Median : 0.000 Median : 0.00 Median : 0.000
## Mean : 23.04 Mean : 2.598 Mean : 16.37 Mean : 2.501
## 3rd Qu.: 0.00 3rd Qu.: 0.000 3rd Qu.: 0.00 3rd Qu.: 0.000
## Max. :1012.00 Max. :508.000 Max. :576.00 Max. :800.000
##
## PoolQC Fence MiscFeature MiscVal
## Length:2628 Length:2628 Length:2628 Min. : 0.00
## Class :character Class :character Class :character 1st Qu.: 0.00
## Mode :character Mode :character Mode :character Median : 0.00
## Mean : 54.74
## 3rd Qu.: 0.00
## Max. :17000.00
##
## MoSold YrSold SaleType SaleCondition
## Min. : 1.000 Min. :2006 Length:2628 Length:2628
## 1st Qu.: 4.000 1st Qu.:2007 Class :character Class :character
## Median : 6.000 Median :2008 Mode :character Mode :character
## Mean : 6.211 Mean :2008
## 3rd Qu.: 8.000 3rd Qu.:2009
## Max. :12.000 Max. :2010
##
## SalePrice
## Min. : 34900
## 1st Qu.:154975
## Median :176520
## Mean :180058
## 3rd Qu.:191657
## Max. :755000
##
A continuación, dividiremos las variables en las siguientes categorías, para facilitar su estudio.
El siguiente código realiza la descomposición en dichas categorías para usar con posterioridad:
var_continuas <- c('LotFrontage','LotArea','MasVnrArea','BsmtFinSF1','BsmtFinSF2','BsmtUnfSF','TotalBsmtSF','1stFlrSF','2ndFlrSF','LowQualFinSF','GrLivArea','GarageArea','WoodDeckSF','OpenPorchSF','EnclosedPorch', '3SsnPorch','ScreenPorch','PoolArea','MiscVal','SalePrice')
var_discretas <- c('BsmtFullBath','BsmtHalfBath','FullBath','HalfBath','BedroomAbvGr','KitchenAbvGr','TotRmsAbvGrd','Fireplaces','GarageCars','GarageYrBlt','YearBuilt','YearRemodAdd','YrSold','MoSold')
var_ordinales <- c('LotShape','Utilities','LandSlope','OverallQual','OverallCond','ExterQual','ExterCond','BsmtQual','BsmtCond','BsmtExposure','BsmtFinType1','BsmtFinType2','HeatingQC','CentralAir','Electrical','KitchenQual','Functional','FireplaceQu','GarageFinish','GarageQual','GarageCond','PavedDrive','PoolQC','Fence')
var_nominales <- c('MSSubClass','MSZoning','Street','Alley','LandContour','LotConfig','Neighborhood','Condition1','Condition2','BldgType','HouseStyle','RoofStyle','RoofMatl','Exterior1st',
'Exterior2nd','MasVnrType','Foundation','Heating','GarageType','MiscFeature', 'SaleType','SaleCondition')
Variables continuas:
missing_data_continuas <- train %>% dplyr::select(var_continuas) %>% plot_missing(theme_config = list(legend.position = "bottom", axis.text.x =element_text(angle = 90),axis.text.y = element_text(size = rel(1))))
Variables discretas:
missing_data_discretas <- train %>% dplyr::select(var_discretas) %>% plot_missing(theme_config = list(legend.position = "bottom", axis.text.x =element_text(angle = 90),axis.text.y = element_text(size = rel(1))))
Variables discretas ordinales:
missing_data_ordinales <- train %>% dplyr::select(var_ordinales) %>% plot_missing(theme_config = list(legend.position = "bottom", axis.text.x =element_text(angle = 90),axis.text.y = element_text(size = rel(1))))
Variables nominales:
missing_data_nominales <- train %>% dplyr::select(var_nominales) %>% plot_missing(theme_config = list(legend.position = "bottom", axis.text.x =element_text(angle = 90),axis.text.y = element_text(size = rel(1))))
Como hay muchas variables que no tienen valores faltantes, vamos a crear un dataset únicamente con las columnas que tienen missings para poder analizar mejor los datos.
var_missing <- c(var_missing_continuas, var_missing_discretas, var_missing_nominales, var_missing_ordinales)
train_na <- train %>% dplyr::select(var_missing)
na_counts <- sapply(train_na, function(x) sum(is.na(x)))
na_counts_sort <- sort(na_counts, decreasing = TRUE)
(na_counts_sort)
## LotFrontage GarageYrBlt MasVnrType MasVnrArea BsmtFullBath BsmtHalfBath
## 314 96 17 16 2 2
## MSZoning BsmtFinSF1 BsmtFinSF2 BsmtUnfSF TotalBsmtSF GarageArea
## 2 1 1 1 1 1
## GarageCars SaleType Exterior2nd Exterior1st Electrical KitchenQual
## 1 1 1 1 1 1
aggr_plot <- aggr(train_na,col=c('navyblue','red'), numbers=TRUE, sortVars=TRUE, cex.axis=.7, gap=3, ylab=c("Histogram of missing data","Pattern"))
##
## Variables sorted by number of missings:
## Variable Count
## LotFrontage 0.1707449701
## GarageYrBlt 0.0522022838
## MasVnrType 0.0092441544
## MasVnrArea 0.0087003806
## BsmtFullBath 0.0010875476
## BsmtHalfBath 0.0010875476
## MSZoning 0.0010875476
## BsmtFinSF1 0.0005437738
## BsmtFinSF2 0.0005437738
## BsmtUnfSF 0.0005437738
## TotalBsmtSF 0.0005437738
## GarageArea 0.0005437738
## GarageCars 0.0005437738
## SaleType 0.0005437738
## Exterior2nd 0.0005437738
## Exterior1st 0.0005437738
## Electrical 0.0005437738
## KitchenQual 0.0005437738
Ahora podemos prestar verdadera atención a las columnas con datos faltantes.
Si nos fijamos en la columna que más datos faltantes tiene, podemos ver en la descripción del dataset que son la cantidad de pies lineales que hay de acera hasta la puerta de la casa. Además, leyendo la documentación del dataset vemos que los NA’s no significa que no tengan conexión con la acera (un edificio de varios pisos por ejemplo), sino que son datos faltantes.
Hemos considerado que una variable con más de un 15% de datos faltantes nos va a desbalancear el dataset drásticamente, por lo que vamos a prescindir de este valor para nuestro estudio y para la imputación de missings, por lo que la borramos del dataset.
Para el caso en el que la proporción de datos faltantes sea menor que el 3% (0.03) imputaremos a los valores faltantes de variables cuantitativas su mediana o media, mientras que a las variables categóricas les imputaremos la categoría más frecuente.
Para las columnas en que la proporcion de datos faltantes sea superior al 3% (0.03) tendremos que utilizar técnicas de imputación más avanzadas. Si en nuestro dataset se diera el caso en el que existen varias columnas de este último tipo habría que realizar un análisis de sensibilidad para comprobar que no hemos alterado la naturaleza del dataset imputando los datos faltantes.
Solo nos faltaría una variable por imputar sus valores: GarageYrBlt. Esta variable indica el año en el que se construyó el garaje de la propiedad. No lo vamos a imputar con el valor medio ya que la proporción de datos faltantes es mayor al 3%, así que lo que proponemos para su imputación es lo siguiente:
Como conocemos el vecindario al que pertenecen los pisos, es lógico que el año de construcción del garaje sea similar para todo el vecindario. Vamos a proceder a agrupar los garages por vecindario e imputaremos el año medio correspondiente.
Y ya tendríamos todas las columnas con datos faltantes imputadas.
Ahora comenzaremos con el análisis univariante de los datos del dataset. Empezaremos dicho análisis mostrando un conjunto de histogramas.
Variables nominales:
train %>% dplyr::select(var_nominales) %>% plot_bar(nrow =2,ncol=2)
A la vista de estos histogramas, observamos lo siguiente:
gr1 <- ggplot(train[!is.na(train$SalePrice),], aes(x=Neighborhood, y=SalePrice)) +
geom_bar(stat='summary', fun.y = "median", fill='blue') +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
scale_y_continuous(breaks= seq(0, 1800000, by=20000), labels = comma) +
geom_label(stat = "count", aes(label = ..count.., y = ..count..), size=3) +
geom_hline(yintercept=150000, linetype="dashed", color = "red")
gr2 <- ggplot(data=train, aes(x=Neighborhood)) +
geom_histogram(stat='count')+
geom_label(stat = "count", aes(label = ..count.., y = ..count..), size=3)+
theme(axis.text.x = element_text(angle = 45, hjust = 1))
grid.arrange(gr1, gr2)
Una posible manera de quitarnos esta molesta variable es discretizándola por un umbral de precio de venta medio en la zona. Podríamos llamar a las dos colecciones “zona cara” y “zona normal”.
La otra variable categórica que se podría tratar de la misma forma que la anterior es una varible que clasifica a la casa según un cojunto cerrado de valores que a priori no tiene un significado claro para nosotros. Mostramos las mismas gráficas para esta variable para ver si se puede seguir una estrategia similar a la de la anterior variable:
gr1 <- ggplot(train[!is.na(train$SalePrice),], aes(x=MSSubClass, y=SalePrice)) +
geom_bar(stat='summary', fun.y = "median", fill='blue') +
theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
scale_y_continuous(breaks= seq(0, 800000, by=50000), labels = comma) +
geom_label(stat = "count", aes(label = ..count.., y = ..count..), size=3) +
geom_hline(yintercept=150000, linetype="dashed", color = "red")
gr2 <- ggplot(data=train, aes(x=MSSubClass)) +
geom_histogram(stat='count')+
geom_label(stat = "count", aes(label = ..count.., y = ..count..), size=3) +
theme(axis.text.x = element_text(angle = 45, hjust = 1))
grid.arrange(gr1, gr2)
Se observa que todas las distribuciones continen un alto grado de asimetría (Skewness), cosa que habrá que tener en cuenta por si hay que realizar algún tipo de transformación sobre ellas en futuros apartados de la práctica.
Existe una gran cantidad de variables que tienen una varianza muy reducida, tales como Street, Alley, LandContour, Condition1, Condition2, RoofStyle, RoofMtl, Heating, GarageType, SaleCondition.
Estas variables pueden ser problemáticas al realizar el proceso de regresión lineal para el que este tipo de variables puede ser problemático, por no contar que para estos modelos este tipo de variables tienen muy poco poder predictivo.
El siguiente código detectara las variables que tienen baja varianza e intentara eliminar todas aquellas que no sean significativas en una regresión de ella con la variable salePrice.
elementosBajaVarianza <- nearZeroVar(train,freqCut = 95/5,uniqueCut = 10)
var_eliminar_correlacion <- c()
# estas columna dan problemas, la varible 3SnnPorch empieza por un número y a la hora de ejecutar el modelo para ver si son significativas en la regresión
var_to_remove = c(var_to_remove,'3SsnPorch')
for(i in colnames(train)[elementosBajaVarianza]) {
if(i != '3SsnPorch') {
modelRegression <- lm(reformulate(termlabels = i, response = 'SalePrice') ,data=train)
if(lmp(modelRegression) >.05) {
var_eliminar_correlacion = c(var_eliminar_correlacion,i)
}
}
}
var_to_remove <- c(var_to_remove,var_eliminar_correlacion)
Las siguientes variables se eliminaran del dataset:
(var_eliminar_correlacion)
## [1] "Street" "Utilities" "LandSlope" "Condition2" "LowQualFinSF"
## [6] "Functional" "MiscFeature" "MiscVal"
Variables ordinales:
train %>% dplyr::select(var_ordinales) %>% plot_bar(nrow =2,ncol=2)
No son otra cosa que variables categóricas pero que encierran una relación de orden interna. El tratamiento de estas variables para transformarlas en categóricas y cambiar el ordenamiento alfabético que R les da por defecto.
Se observan práctimante las mismas anomalías en en el caso anterior, variables con distribuciones muy alejadas de la normalidad y en muchos casos dichas variables apenas aportan información debido a que la mayoría de los valores están concentrados en una única categoría.
Variables contínuas:
var_continuas <- c('LotArea','MasVnrArea','BsmtFinSF1','BsmtFinSF2','BsmtUnfSF','TotalBsmtSF','1stFlrSF','2ndFlrSF','LowQualFinSF','GrLivArea','GarageArea','WoodDeckSF','OpenPorchSF','EnclosedPorch', '3SsnPorch','ScreenPorch','PoolArea','MiscVal','SalePrice')
train %>% dplyr::select(var_continuas) %>% plot_histogram(nrow =2,ncol=2)
Los comentarios que se pueden hacer en este caso son los siguientes:
Muy pocas variables parecen seguir una distribución normal y casi todas tienen un fuerte sesgo hacia la izquierda (y unas pocas a la derecha), sugiriendo para muchas de ellas una transformación de tipo logarítmico. Además, algunas de ellas parecen presentar numerosos “outliers” debido a que el rango de variación de las mismas es muy elevado en una gran parte de los valores finales sin que aparezcan casi valores en los mismos.
La variable “Id” es una especie de clave primária organizadora de las filas y hay que eliminarla en los procesos de regresión.
var_to_remove <- c(var_to_remove,'Id')
Nos centraremos ahora en mostrar con mas detalle los histograma y diagramas de densidad de las variables que describen el tamaño en superficie de cada parte en la que se compone una casa:
(Pc-> Pies cuadrados) (Mc-> Metros cuadrados) (Rooms-> Habitaciones)
s1 <- ggplot(data= train, aes(x=GrLivArea)) +
geom_density() + labs(x='Pc habitables')
s2 <- ggplot(data=train, aes(x=as.factor(TotRmsAbvGrd))) +
geom_histogram(stat='count') + labs(x='nº rooms encima del sótano')
s3 <- ggplot(data= train, aes(x=`1stFlrSF`)) +
geom_density() + labs(x='Pc - 1ª planta')
s4 <- ggplot(data= train, aes(x=`2ndFlrSF`)) +
geom_density() + labs(x='Pc - 2ª planta')
s5 <- ggplot(data= train, aes(x=TotalBsmtSF)) +
geom_density() + labs(x='Mc - sótano')
s6 <- ggplot(data= train[train$LotArea<100000,], aes(x=LotArea)) +
geom_density() + labs(x='Mc - jardin')
#s7 <- ggplot(data= train, aes(x=LotFrontage)) +
# geom_density() + labs(x='Tamaño en metros cuadrados del jardin frontal')
s8 <- ggplot(data= train, aes(x=LowQualFinSF)) +
geom_histogram() + labs(x='Espacio no habitable 1ª y 2ª')
#layout <- matrix(c(1,2,5,3,4,8,6,7),4,2,byrow=TRUE)
grid.arrange(s1, s2, s3, s4, s5, s6, s8)
Variables discretas:
train %>% dplyr::select(var_discretas) %>% plot_histogram(nrow =2,ncol=2)
Comentarios parecidos a el análisis de distribución de las variables contínuas se pueden hacer para estas variables. No obstante merece la pena recalcar que algunas de estas variables (YearBuilt,YearRemodAdd y YrSold) codifican años. En estos casos, para simplificar el modelo y debido a la irregular distribución de las mismas, se dividirán estas variables en rangos (de, por ejemplo, 20 años) y se creará una faceta representando esta división en el apartado de tratamiento de variables cuantitativas.
Variable SalePrice:
Ahora nos ocuparemos del análisis de la variable dependiente de nuestro modelo, el precio de las casas. En primer lugar dibujaremos su histograma:
ggplot(train, aes(x=SalePrice)) +
geom_histogram(aes(y=..density..),
binwidth=7000,
colour="black", fill="white") +
geom_density(alpha=.2, fill="#FF6666")
Vemos que esta variable tiene un importe sesgo a la izquierda y parece que un cambio de variable de tipo logarítmico podría convertirla en una variable con distribución normal, cosa importante para el modelo lineal de regresión múltiple. El sesgo es muy fácilmente explicable por la naturaleza de la propia variable: la mayoría de las casas deberán tener un precio ajustado a la distribución de la riqueza entre los habitantes de la ciudad, y muy pocas tendrán un gran valor (casas lujosas) o serán infraviviendas. Confirmemos esta hipótesis realizando el cambio de variable en la misma y dibujando su función de distribución normal aproximada:
fitDistribution <- fitdistr(log10(train$SalePrice), densfun = "normal")
ggplot(data = train) +
geom_histogram(mapping = aes(x = log10(SalePrice), y = ..density..), col="white") +
stat_function(fun = dnorm, args = list(mean = fitDistribution$estimate[1], sd = fitDistribution$estimate[2], log = F),
color="red", lwd=1)
Vemos que, efectivamente, lo que hemos comentado en el anterior apartado se cumple. Detectamos también que el rango dinámico de la variable es muy grande en su parte derecha sin que apenas haya valores en dicho intervalo, lo que invita a pensar en la presencia de “outliers” (casas de un gran valor económico reservadas a la gente más rica de la ciudad). Dibujaremos sendos diagramas de cajas con la variable original y la variable transformada:
graf3 <- train %>% dplyr::select(SalePrice) %>%
na.omit() %>%
ggplot(aes(x=0,y=SalePrice)) +
geom_boxplot(outlier.colour="black", outlier.shape=16,
outlier.size=2, notch=FALSE)
graf4 <- train %>% dplyr::mutate(log10_saleprice = log10(SalePrice)) %>%
dplyr::select(log10_saleprice) %>%
na.omit() %>%
ggplot(aes(x=0,y=log10_saleprice)) +
geom_boxplot(outlier.colour="black", outlier.shape=16,
outlier.size=2, notch=FALSE)
ggarrange(graf3,graf4)
Vemos claramente la existencia de Outliers en esta variable que la transformación logarítmica no es capaz de resolver, tanto en los valores superiores como en los inferiores. En una parte futura de este apartado, detectaremos y almacenaremos en sendas variables estos Outliers para todas las variables independientes y tomar una decisión de cómo transformales en los apartados de tratamiento de variables cualitativas y cuantitativas.
Graficos QQ - Variables continuas y discretas
Ahora realizaremos un conjunto de gráficos cuantil/cuantil sobre las variables continuas y discretas para corroborar que hay pocas variables con una distribución normal.
train %>% plot_qq(sampled_rows = 1000L, nrow =2,ncol=2)
Las que más se aproximan a esta distribución son: LotArea, LotFrontage, GrLivArea, TotalBsmtSF y X1stFlrSF. Este diagrama pone en evidencia también la gran cantidad de Outliers que tienen algunas variables.
Almacenaremos el grado de asimetría de dichas variables para en un futuro poder tener un criterio más claro de qué variables son candidatas a un tipo de transformación logarítmica.
var_continuas_discretas = c(var_continuas,var_discretas)
listasVariablesSkewness <- na.omit(train %>% dplyr::select(var_continuas_discretas)) %>% skewness()
variablesSkewnessAlto <- listasVariablesSkewness[abs(listasVariablesSkewness) > 0.8]
Ahora procedermos a realizar el análisis vivariante de nuestros datos. Empezaremos con analizar los coeficientes de correlación entre las distintas variables. El siguiente diagrama intenta dibujar un mapa de calor de dichas correlaciones para las variables contínuas, pero debido a la gran cantidad de varibles en dicho diagrama no se puede visualizar nada con claridad:
splitDataset <- split_columns(train, binary_as_factor = FALSE)
plot_correlation(na.omit(splitDataset$continuous), type = "c" ,maxcat = 5L,theme_config = list(legend.position = "bottom", axis.text.x =element_text(angle = 90),axis.text.y = element_text(size = rel(0.4)) ))
Matriz de correlaciones - Variables continuas
Crearemos una matriz de correlaciones con las variables continuas para intentar evidencia de mejor forma estos posibles casos de variables fuertemente correlacionadas que podrían desestabilizar el resultado del proceso de regresión lineal:
corrmatrix <-corrr::correlate(na.omit(train %>% dplyr::select(var_continuas)))
rplot(corrmatrix, legend = TRUE, shape = 16,print_cor = TRUE) +theme(axis.text.x = element_text(angle = 90))
Con esta matriz se observan lo siguiente:
La mayor parte de las variables representan una correlación positiva con el precio de la vivienda y entre ellas. Eso es normal debido a que muchas representan cantidades (pies cuadrados en las distintas zonas en las que se descompone las estancias de la vivienda, número y tamaño de cada planta, tamaño de garage y sótanos, etc. ). Evidentemente, cuandos mayores son estos números, normalmente mayor será el valor de la vivienda.
Las variables 1stFlrSF y TotalBsmtSF estan fuertemente correladas, habría que quitar una de ellas en el modelo (es normal que haya una correlacion entre la superficie total del sótano y la de la primera planta)
Existen correlaciones moderadas entre las variables GrLivArea y 1stFlrSF y 2ndFlrSF, no obstante un análisis más detentenido nos muestra que la suma de las siguientes variables tienen correlacion unidad, lo cuál es un punto a favor en la consistencia de los datos de estas variables en el dataset:
cor(train$GrLivArea, (train$`1stFlrSF` + train$`2ndFlrSF` + train$LowQualFinSF))
## [1] 1
Esto es lógico debido a que la suma de las superficies habitables de la primera y segunda planta mas la superficie de “baja calidad” tienen que coicidir con la superficie total habitable sin contar los sótanos. Es por ello que estas variables o hay que transformarlas para evitar este caso de correlación perfecta o bien eliminar del modelo alguna de ellas. La decisión se tomará mas adelante.
A continuación mostraremos un tipo de gráfica que muestra de forma más visual la anterior matriz de correlación. En ella cuando más cerca están unos puntos de otros existe una mayor similitud entre las distintas variables. Cada una de ellas está a su vez enlazada con una “banda” de color azul o rojo que cuantifica el grado de correlación entre las variables. En ella ya vemos que la variable objetivo de estudio, el precio de venta de la casa, está muy influenciada por todas aquellas variables continuas que cuantifican la superficie en la que se dividen las distintas parcelas de la propiedad (habitaciones, garage, porche, sótano, etc.).
network_plot(corrmatrix)
Matriz de correlaciones - Variables discretas
Ahora proseguiremos el análisis con las variables discretas. Como antes, generaremos una matriz de correlación y estudiaremos los resultados:
corrmatrix2 <-corrr::correlate(na.omit(train %>% dplyr::select(var_discretas)))
rplot(corrmatrix2, legend = TRUE, shape = 16,print_cor = TRUE)+theme(axis.text.x = element_text(angle = 90))
network_plot(corrmatrix2)
Matriz de correlaciones - Variables continuas y discretas
Ahora intentaremos hacer este mismo análisis agrupando tanto variables contínuas como discretas, El resultado es el siguiente:
corrmatrix3 <-corrr::correlate(na.omit(train %>% dplyr::select(var_continuas_discretas)))
rplot(corrmatrix3, legend = TRUE, shape = 16,print_cor = TRUE)+theme(axis.text.x = element_text(angle = 90))
network_plot(corrmatrix3)
Aqui Observamos una correlación muy fuerte entre las variables GarageCars y GarageArea, con lo que nos podemos quitar la variable GarageCars.Es lógico pensar que el número de coches que caben en una plaza de garage dependa de la superficie de ésta.
Existe una correlacion muy fuerte también entre las variables GrLiveArea y TotRmsAbvGrd (total de habitaciones), con lo que podemos quitar esta última del análisis al parecer más sencilla de entender la primera de las variables.
Hay una fuerte correlación entre las variables YearRemodAdd y las variables YearBuild y GarageYrBuild. Esto indica que en muchas de ellas el año en que se construyó el garage coincide con el año en que se contruyó la casa. En el caso de la variable YearRemodAdd parece que se ha hecho coincidir con el año de construcción si la casa no ha sufrido reformas.
La siguiente función confirmará nuestras sospechas, buscando variables con fuertes correlaciones entre ellas:
correlacionesProblematicas <- findCorrelation(cor(na.omit(train %>% dplyr::select(var_continuas_discretas))),cutoff = 0.8, verbose = FALSE, names = TRUE)
(correlacionesProblematicas)
## [1] "1stFlrSF" "GarageCars" "GarageYrBlt"
Comprobamos que, efectivamente, las variables antes comentadas están entre las que aparecen en el listado.
Correlación SalesPrice
Ahora intentaremos mostrar en un gráfico de barras el grado de correlación entre las variables anteriormente analizadas y la variable objeto de nuestro análisis.
correlacionesSalesPrice <-focus(corrmatrix3, SalePrice)
correlacionesSalesPrice%>% arrange(SalePrice) %>% ggplot(aes(x=rowname ,y=SalePrice,fill=factor(rowname))) +
geom_bar(position="dodge",stat="identity") +
coord_flip() +
ggtitle("Correlacion entre las variables numéricas y el precio de las casas")
En este diagrama se muestra más claramente lo que ya se ha comentado con anterioridad: casi todas las variables tienen correlaciones positivas con el precio excepto unas pocas (sería interesante el averiguar por qué, ya que serían factores que influyen negativamente en el precio de una vivienda). Además las variables con mayores correlaciones tiene que ver con la superficie habitable de la casa y la superficie de la primera planta (lo que muestra que la mayoría de las casas tienen una única planta. El tener una segunda planta influye moderadamente en el precio de la vivienda). También hay correlaciones moderadas entre las características del garage, el año de construcción, la superficie del sótano, etc. Por lo tanto, estas variables deberían entrar significativamente en el modelo de regresión que generemos en esta práctica.
Ahora realizaremos unos cuantos diagramas de dispersión entre las variables contínuas y el precio de la vivienda para ver si existen relaciones lineales entre dichas variables (importante para saber si el modelo de regresión lineal es el más adecuado para el problema que tenemos en esta práctica):
na.omit(train %>% dplyr::select(var_continuas)) %>% plot_scatterplot( by = "SalePrice", sampled_rows = 1000L, nrow =2,ncol=2)
Observamos que sí que existen ciertas relaciones apróximadas pero con algunas anomalías que comentamos a continuación:
Existen muchos valores concentrados en el valor cero para muchas de las variables (es normal, por ejemplo, que haya muchas casas que no tengan segundo piso, por lo que para ellas la variable que contabiliza su superficie sea cero).
Se ven numerosos “outliers” en muchas variables, lo que es indicativo de casas con características especiales, tanto a la baja como al alza. En particular, la variable “LotArea” es un indicativo de casas de lujo para valores extremadamente altos de la misma (en su gráfica se muestran claramente estos “outliers”). Otra de las variables que muestran casas fuera de lo común es “3SsnPorch”, en donde vemos que muy pocas casas la tienen informada con un valor mayor que cero.
En cuanto a la detección de Outliers, como se ha indicado con anterioridad, existen indicios más que suficientes para inferir que, efectivamente, en este dataset existe más de una casa que podría crear problemas en el modelo de regresión lineal. Para detectarlos y almacenarlos en una lista para su posterior tratamiento, ejecutaremos el siguiente código, que además mostrará un conjunto de diagramas de caja con las variables continuas del dataset:
listaOutliers <- list()
#listaBoxPlots <- c()
dataset_var_continuas <- train %>% dplyr::select(var_continuas)
iterator = 1
for(i in colnames(dataset_var_continuas)) {
outlier_values <- boxplot.stats(dataset_var_continuas[[i]])$out
boxPlotDibujar <- boxplot(dataset_var_continuas[[i]], main=i, boxwex=0.1)
if(i %in% c('LotArea','1stFlrSF','GrLivArea','PoolArea','TotalBsmtSF')) {
boxPlotDibujar
}
#listaBoxPlots <- c(listaBoxPlots,boxPlotDibujar)
outliersRosen <- rosnerTest(dataset_var_continuas[[i]], k = 4, warn = F)
listaOutliers[[iterator]] <- outliersRosen
iterator <- iterator +1
}